#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <map>
#include <stdint.h>
#include <stdio.h>
#include<stdlib.h>

#include "code.cpp"

#ifndef BASE
#define BASE
#include "base.cpp"
#endif

#ifndef CONSTANT
#define CONSTANT
#include "constant.cpp"
#endif
#include "attribute.cpp"

#define t_slot slot_32

using namespace std;

class ClassDefinition;
class Heap;
AttributeInfo* parseAttribute(ClassIter *itr, ClassDefinition* cls_def);


const uint16_t 
acc_public = 0x1, acc_private = 0x2, acc_protected = 0x4,
acc_static = 0x8, acc_final = 0x10, acc_synchronized = 0x20,
acc_bridge = 0x40, acc_varargs = 0x80, acc_native = 0x100,
acc_abstract = 0x400, acc_strictfp = 0x800, acc_synthetic = 0x1000;

const string* main_method_name;

struct slot_64{
    uint32_t hi;
    uint32_t low;
};

struct slot_32 {
    uint32_t data;
};

//字段表
class FieldInfo {
    public:
    uint16_t access_flags;
    uint16_t name_index;
    uint16_t descriptor_index;
    uint16_t attributes_count;
    vector<AttributeInfo*> attributes = vector<AttributeInfo*>();
};

class MethodInfo {
    public:
    uint16_t access_flags;
    uint16_t name_idx;
    uint16_t descriptor_idx;
    uint16_t attr_count;
    map<int,AttributeInfo*> attrs;
    CodeAttr* codeAttr;
    int code_len;
    MethodInfo(){
        attrs = map<int,AttributeInfo*>();
    }

    const uint8_t* getCodes(){
        return codeAttr->codes;
    }

    void print(const vector<ConstantInfo*> *pool) {

    }

};



class ClassDefinition {
    public:
    uint32_t minor_version;
    uint32_t major_version;
    uint32_t constant_pool_count;
    uint32_t acess_flags;
    uint16_t fields_count;
    uint16_t methods_count;
    vector<ConstantInfo*> constant_pool = vector<ConstantInfo*>();
    vector<uint16_t> interfaceIndexs = vector<uint16_t>(); ;
    vector<FieldInfo*> fieldInfos = vector<FieldInfo*>();
    vector<MethodInfo*> methodInfos = vector<MethodInfo*>();

    void addConstantInfo(ConstantInfo *info) {
        constant_pool.push_back(info);
    }
    ConstantInfo* getConstantInfo(int index) {
        return constant_pool[index - 1];
    }

    void print() {
        printf("Constant pool:\n");
        for(int i = 0; i < constant_pool_count - 1; i++) {
            auto itr = constant_pool[i];
            itr->print(&constant_pool);
        }
        printf("{\n");
        for(int i = 0; i < methods_count; i++) {
            auto itr = methodInfos[i];
            itr->print(&constant_pool);
        }
        printf("}\n");

    }

};


class ClassLoader {
    public:

    ClassDefinition *loadClass(const char *path) {
        FILE *fp;
        fp = fopen(path, "r");
        if(!fp) {
            printf("error: open class fail: %s", path);
        }

        ClassIter *clsItr = new ClassIter(fp);

        if(clsItr->nextu4() == 0xCAFEBABE) {
            printf("[%s] is java classFile.\n", path);
        }
    
        /* printf("minor version: %d\n", nextu2(fp));
        printf("major version: %d\n", nextu2(fp)); */
        ClassDefinition *cls_def = new ClassDefinition();
        cls_def->minor_version = clsItr->nextu2();
        cls_def->major_version = clsItr->nextu2();
        uint32_t constant_pool_count = clsItr->nextu2();
        printf("pool count %d\n", constant_pool_count);
        //解析常量池
        cls_def->constant_pool_count = constant_pool_count;
        for (int i = 1; i < constant_pool_count; i++)
        {
            uint8_t tag = clsItr->nextu1();
            // printf("parse constant_pool, tag: %d\n", tag);
            // 每个类里parse，还是放在这里case好？？？？
            // 先放着，待它显现出不好的时候在进行重构
            // 个人认为还是放这里，因为解析class文件生成常量池实例只会在这里使用
            // 放在类里又不会有其他地方重用
            switch(tag) {
                case (const_utf8_tag): {
                    ConstantUtf8Info *info = new ConstantUtf8Info();
                    info->tag = tag;
                    info->order = i;
                    info->length = clsItr->nextu2();
                    info->bytes = clsItr->nextuNString(info->length);
                    // printf("utf8 constant len:%d, str: %s\n", info->length,info->bytes->c_str());
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_integer_tag): {
                    ConstantIntegerInfo *info = new ConstantIntegerInfo();
                    info->tag = tag;
                    info->order = i;
                    *(info->bytes) = clsItr->nextu4();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_float_tag): {
                    ConstantFloatInfo *info = new ConstantFloatInfo();
                    info->tag = tag;
                    info->order = i;
                    *(info->bytes) = clsItr->nextu4();
                    cls_def->constant_pool.push_back(info);

                }
                break;
                case (const_long_tag): {
                    ConstantLongInfo *info = new ConstantLongInfo();
                    info->tag = tag;
                    info->order = i;
                    *(info->bytes) = clsItr->nextu4();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_double_tag): {
                    ConstantDoubleInfo *info = new ConstantDoubleInfo();
                    info->tag = tag;
                    info->order = i;
                    *(info->bytes) = clsItr->nextu4();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_class_tag): { 
                    ConstantClassInfo *info = new ConstantClassInfo();
                    info->tag = tag;
                    info->order = i;
                    info->index = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_string_tag):{ 
                    ConstantStringInfo *info = new ConstantStringInfo();
                    info->tag = tag;
                    info->order = i;
                    info->index = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_fieldref_tag):{ 
                    ConstantFieldrefInfo *info = new ConstantFieldrefInfo();
                    info->tag = tag;
                    info->order = i;
                    info->clsIdx = clsItr->nextu2();
                    info->typeIdx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_nameAndType_tag):{ 
                    ConstantNameAndTypeInfo *info = new ConstantNameAndTypeInfo();
                    info->tag = tag;
                    info->order = i;
                    info->idx1 = clsItr->nextu2();
                    info->idx2 = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_methodref_tag): {
                    ConstantMethodrefInfo *info = new ConstantMethodrefInfo();
                    info->tag = tag;
                    info->order = i;
                    info->clsIdx = clsItr->nextu2();
                    info->typeIdx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_interfaceMethodref_tag):{
                    ConstantInterfaceMethodrefInfo *info = new ConstantInterfaceMethodrefInfo();
                    info->tag = tag;
                    info->order = i;
                    info->clsIdx = clsItr->nextu2();
                    info->typeIdx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_MethodHandle_tag): {
                    ConstantMethodHandleInfo *info = new ConstantMethodHandleInfo();
                    info->tag = tag;
                    info->order = i;
                    info->ref_kind = clsItr->nextu1();
                    info->ref_idx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_methodType_tag): {
                    ConstantMethodTypeInfo *info = new ConstantMethodTypeInfo();
                    info->tag = tag;
                    info->order = i;
                    info->descriptor_idx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                case (const_InvokeDynamic_tag): {
                    ConstantDynamicInfo *info = new ConstantDynamicInfo();
                    info->tag = tag;
                    info->order = i;
                    info->bootstrap_method_attr_index = clsItr->nextu2();
                    info->name_and_type_idx = clsItr->nextu2();
                    cls_def->constant_pool.push_back(info);
                }
                break;
                default: {
                    printf("unsupport constant pool tag : %d\n", tag); 
                    delete cls_def;
                    exit(1);
                }
                break;

            }
        }

        //访问标志
        uint16_t access_flags = clsItr->nextu2();
        //指向class常量
        uint16_t thisClsIndex = clsItr->nextu2();
        uint16_t superClsIndex = clsItr->nextu2();

        //接口索引
        uint16_t interfaceCount = clsItr->nextu2();
        for(int i = 0; i < interfaceCount; i++) {
            uint16_t index = clsItr->nextu2();
            cls_def->interfaceIndexs.push_back(index);
        }
        //字段表
        uint16_t field_count = clsItr->nextu2();
        // printf("field count: %d\n", field_count);
        for(int i = 0; i < field_count; i++) {
            FieldInfo *f = new FieldInfo();
            f->access_flags = clsItr->nextu2();
            f->name_index = clsItr->nextu2();
            f->descriptor_index = clsItr->nextu2();
            uint16_t count = clsItr->nextu2();
            f->attributes_count = count;
            while(count--) {
                AttributeInfo *a = parseAttribute(clsItr, cls_def);
                f->attributes.push_back(a);
            }

        }

        //方法表
        uint16_t method_count = clsItr->nextu2();
        cls_def->methods_count = method_count;
        // printf("methods count: %d\n", method_count);
        
        while(method_count--) {
            MethodInfo* info = new MethodInfo();
            info->access_flags = clsItr->nextu2();
            info->name_idx = clsItr->nextu2();
            info->descriptor_idx = clsItr->nextu2();
            int attr_count = clsItr->nextu2();
            info->attr_count = attr_count;
            while(attr_count--) {
                auto attr = parseAttribute(clsItr, cls_def);
                if(attr->type == t_code) {
                    CodeAttr* codeAttr = (CodeAttr*)attr;
                    info->codeAttr = codeAttr;
                    info->code_len = codeAttr->code_len;
                }
                info->attrs.insert(make_pair(attr->type, attr));
            }
            cls_def->methodInfos.push_back(info);
        }
        return cls_def;  
    }


};



AttributeInfo* parseAttribute(ClassIter *itr, ClassDefinition* cls_def) {
    //指向常量池一个utf8的常量
    uint16_t index = itr->nextu2();
    // printf("attribute name constant index : %d\n", index);
    ConstantUtf8Info* attrNameConstant = (ConstantUtf8Info*) cls_def->getConstantInfo(index);
    const string* name = attrNameConstant->bytes;
    
    if(name == attr_code_str) {
        CodeAttr* attr = new CodeAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        attr->max_stack = itr->nextu2();
        attr->max_locals = itr->nextu2();
        attr->code_len = itr->nextu4();
        attr->codes = itr->nextuN(attr->code_len);
        int exception_len = itr->nextu2();
        attr->exception_tbl_len = exception_len;
        while(exception_len--) {
            ExceptionInfo *expInfo = new ExceptionInfo();
            expInfo->start_pc = itr->nextu2();
            expInfo->end_pc = itr->nextu2();
            expInfo->handler_pc = itr->nextu2();
            expInfo->catch_type = itr->nextu2();
            attr->exception_tbl->push_back(expInfo);
        }
        //todo exception table
        uint16_t attr_count = itr-> nextu2();
        for(int i = 0; i < attr_count; i++) {
            AttributeInfo* sub_attr = parseAttribute(itr, cls_def);
            attr->attrs->push_back(sub_attr);
        }
        return attr;
    }
    if(name == attr_constantVal_str) {
        auto *attr = new ConstantValueAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        attr->constantVal_idx = itr->nextu2();
        return attr;
    }
    if(name == attr_exceptions_str) {
        auto *attr = new ExceptionAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        attr->exception_num = itr->nextu2();
        attr->exception_idx = itr->nextu2();
        return attr;
    }
    if(name == attr_lineNumberTable_str) {
        auto *attr = new LineNumberTable();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        int len = itr->nextu2();
        attr->line_num_tbl_len = len;
        while(len--) {
            LineNumberInfo *info = new LineNumberInfo();
            info->start_pc = itr->nextu2();
            info->line_number = itr->nextu2();
            attr->infos->push_back(info);
        }
        return attr;
    }
    if(name == attr_constantVal_str) {
        auto *attr = new ConstantValueAttr();  
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        attr->constantVal_idx = itr->nextu2();
        return attr;
    }
    if(name == attr_innerClasses_str) {
        auto *attr = new InnerClassesAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        int num = itr->nextu2();
        attr->classes_num = num;

        while(num--) {
            InnerClassesInfo* info = new InnerClassesInfo();
            info->inner_class_idx = itr->nextu2();
            info->outer_class_idx = itr->nextu2();
            info->inner_name_idx = itr->nextu2();
            info->inner_class_access_flags = itr->nextu2();
            attr->infos->push_back(info);
        }
        return attr;
    }
    if(name == attr_deprecated_str) {
        auto* attr = new DeprecatedAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        return attr;
    }
    if(name == attr_synthetic_str) {
        auto* attr = new DeprecatedAttr();
        attr->attr_name_idx = index;
        attr->attr_len = itr->nextu4();
        return attr;
    }
   
    return NULL;
}



class StackFrame {
    public:
    // 操作数栈
    stack<uint64_t> op_stack;
    // 局部变量表  基本单位Slot  存放returnAddress这咋存？
    uint32_t* local_variable;

    // 动态连接、返回地址
    int pc;
    const uint8_t* codes;

    StackFrame(MethodInfo* method) {
        codes = method->getCodes();
        auto code_attr = method->codeAttr;
        local_variable = (uint32_t*) malloc(sizeof(uint32_t) * code_attr->max_locals);
        
    }
 
    void execute(Heap* heap) {
        printf("execute code:");
        while(1) {
            uint8_t onCode = codes[pc++];
            switch(onCode) {
                case nop: {
                    break;
                }
                case aconst_null: {
                    
                    break;
                }
                case iconst_m1: {
                    op_stack.push(-1);
                    break;
                }
                case iconst_0: {
                    op_stack.push(0);
                    break;
                }
                case iconst_1: {
                    op_stack.push(1);
                    break;
                }
                case iconst_2: {
                    op_stack.push(2);
                    break;
                }
                case iconst_3: {
                    op_stack.push(3);
                    break;
                }
                case iconst_4: {
                    op_stack.push(4);
                    break;
                }
                case iconst_5: {
                    op_stack.push(5);
                    break;
                }
                case fconst_0: {
                    op_stack.push(0.0f);
                    break;
                }
                case fconst_1: {
                    op_stack.push(1.0f);
                    break;
                }
                case fconst_2: {
                    op_stack.push(2.0f);
                    break;
                }
                case dconst_0: {
                    op_stack.push(0.0);
                    break;
                }
                case dconst_1: {
                    op_stack.push(1.0);
                    break;
                }  
                case bipush: {
                    op_stack.push(codes[pc++]);
                    break;
                }
                case sipush: {
                    op_stack.push(codes[pc++] << 8 | codes[pc++]);
                    break;
                }
                case ldc: {
                    // int  float ref
                    //todo 

                    uint32_t value = 0;
                    op_stack.push(value);
                    break;
                }
                case ldc_w: {
                    // todo
                    uint32_t value = 0;
                    op_stack.push(value);
                    break;
                }
                case ldc2_w: {
                    // todo
                }
                case iload: {
                    op_stack.push(local_variable[codes[pc++]]);
                    break;
                }
                case dload:
                case lload: {
                    uint64_t value = (uint64_t)local_variable[codes[pc++]] << 32 | local_variable[codes[pc]];
                    op_stack.push(value);
                    break;
                }
                case fload: {
                    op_stack.push(local_variable[codes[pc++]]);
                    break;
                }
                case aload: {
                    op_stack.push(local_variable[codes[pc++]]);
                    break;
                }
                case aload_0: {
                    //从本地变量数组读取index为0的引用到栈顶
                    op_stack.push(local_variable[0]);
                    break;
                }
                case aload_1: {
                    op_stack.push(local_variable[1]);
                    break;
                }
                case aload_2: {
                    //从本地变量数组读取index为0的引用到栈顶
                    op_stack.push(local_variable[2]);
                    break;
                }
                case aload_3: {
                    //从本地变量数组读取index为0的引用到栈顶
                    op_stack.push(local_variable[3]);
                    break;
                }
                // 参数 arrref, index  数组在堆里
                case iaload: {

                }
                case laload: {

                }
                case faload: {

                }
                case daload: {

                }
                case aaload: {

                }
                case baload: {

                }
                case caload: {

                }
                case saload: {

                }
                case astore:
                case fstore:
                case istore: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[codes[pc++]] = v & 0xFFFFFFFF;
                    break;
                }
                case dstore:
                case lstore: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[codes[pc++]] = (v >> 32) & 0xFFFFFFFF;
                    local_variable[codes[pc] + 1] = v & 0xFFFFFFFF;
                    break;
                }
                case istore_0: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[0] = v & 0xFFFFFFFF;
                    break;
                }
                case istore_1: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[1] = v & 0xFFFFFFFF;
                    break;
                }
                case istore_2: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[2] = v & 0xFFFFFFFF;
                    break;
                }
                case istore_3: {
                    uint64_t v = op_stack.top();
                    op_stack.pop();
                    local_variable[3] = v & 0xFFFFFFFF;
                    break;
                }




            }
        }
    }
    

};

class VMstack {
    public:
    vector<StackFrame*> frames = vector<StackFrame*>();
    
};



class MethodArea {
    public:
        

};

class InstanceOopDesc{

};

class DynamicConstantPool {
    public:
    StaticConstantPool* static_pool;
};

class StaticConstantPool {
    public:
    vector<ConstantInfo*> constant_pool = vector<ConstantInfo*>();
    void addConstantInfo(ConstantInfo *info) {
        constant_pool.push_back(info);
    }
    ConstantInfo* getConstantInfo(int index) {
        return constant_pool[index - 1];
    }
};

class Heap {
    public:
    map<int*, InstanceOopDesc*> container = map<int*, InstanceOopDesc*>();  
};

class StackManager {
    public:
    vector<VMstack*> stacks = vector<VMstack*>();
    VMstack* createStack() {

        return NULL;
    }
};

class Engine {
    public:
    map<const char*,ClassDefinition*> *methodArea;
    // VMstack vmStack;
    vector<VMstack*> stacks = vector<VMstack*>();
    StackManager stack_manager = StackManager();
    Heap heap;
    // NativeMethodStack nativeMethodStack;
    //程序计数器

    void load(map<const char*,ClassDefinition*> *classes) {
        methodArea = classes;
    }

    void run(const char* mainClass) {
        MethodInfo* mainMethod = findMainMethod(mainClass);
        //创建Java栈，pc指向字节码
        executeCode(mainMethod);
    }

    MethodInfo* findMainMethod(const char* mainClass) {
        ClassDefinition* classDef = methodArea->find(mainClass)->second;
        auto pool = classDef->constant_pool;
        if(!classDef) {
            printf("cannot find main class.\n");
            return NULL;   
        }
        for(auto item = classDef->methodInfos.begin(), end = classDef->methodInfos.end(); item != end; item++) {
            if(main_method_name == classDef->getConstantInfo((*item)->name_idx)->printStr(&pool)) {
                printf("is main method.\n");
                return *item;
            }
        }
        return NULL;
    }

    int executeCode(MethodInfo* method) {
        auto codeAttr =(CodeAttr*) method->attrs.find(t_code)->second;
        method->getCodes();
        StackFrame* frame = new StackFrame(method);
        VMstack* stack = new VMstack();
        stack->frames.push_back(frame);
        stacks.push_back(stack);

        return 0;
    };

};


void init() {
    str_pool = new unordered_set<string>();
    attr_code_str =NewOrFindString("Code");
    attr_constantVal_str =NewOrFindString("ConstantValue");
    attr_deprecated_str =NewOrFindString("Deprecated");
    attr_exceptions_str =NewOrFindString("Exceptions");
    attr_innerClasses_str =NewOrFindString("InnerClasses");
    attr_lineNumberTable_str =NewOrFindString("LineNumberTable");
    attr_localVariableTable_str =NewOrFindString("LocalVariable");
    attr_sourceFile_str = NewOrFindString("SourceFile");
    attr_synthetic_str =NewOrFindString("Synthetic");
    main_method_name = NewOrFindString("main");
    constant_tag_str[const_utf8_tag] = "Utf8";
    constant_tag_str[const_integer_tag] = "Integer";
    constant_tag_str[const_long_tag] = "Long";
    constant_tag_str[const_float_tag] = "Float";
    constant_tag_str[const_double_tag] = "Double";
    constant_tag_str[const_class_tag] = "Class";
    constant_tag_str[const_string_tag] = "String";
    constant_tag_str[const_fieldref_tag] = "Fieldref";
    constant_tag_str[const_methodref_tag] = "Methodref";
    constant_tag_str[const_interfaceMethodref_tag] = "InterfaceMethodRef";
    constant_tag_str[const_nameAndType_tag] = "NameAndType";
    constant_tag_str[const_methodType_tag] = "MethodType";
    constant_tag_str[const_MethodHandle_tag] = "MethodHandle";
    constant_tag_str[const_InvokeDynamic_tag] = "InvokeDynamic";
    

}


class Sequoia {
    public:

    void run(const char* paths[], const char* enterClass) {
        ClassLoader *clsLoader = new ClassLoader();
        const char* prefix = ".class";
        string clsFile = enterClass;
        clsFile += prefix;
        ClassDefinition* classDef = clsLoader->loadClass(clsFile.c_str());
        classDef->print();

        map<const char*,ClassDefinition*> *classes = new map<const char*,ClassDefinition*>();
        printf("classLoader loading %s\n", enterClass);
        classes->insert(make_pair(enterClass, classDef));
        //todo 后期可优化为懒加载
        Engine engine = Engine();
        engine.load(classes);
        engine.run(enterClass);
    }
    
};

int main(int argc, char const *argv[])
{
    if(argc < 2) {
        printf("usage: sequoia classpath class");
    }
    // xxx.xxx.xxx
    const char* main_class = argv[1];
    // const char* mainFile = *main_class + ".class";
    init();
    Sequoia vm = Sequoia();
    vm.run(NULL, main_class);

    return 0;
}


